package com.reider.log4AS3.core
{
	import com.reider.log4AS3.appenders.Appender;
	import com.reider.log4AS3.dataholder.Level;
	import com.reider.log4AS3.dataholder.LevelHolder;
	import com.reider.log4AS3.dataholder.LoggerScope;
	import com.reider.log4AS3.layouts.Layout;
	import com.reider.log4AS3.utils.LogUtils;
	
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	
	
	/**
	 * @private
	 */
	
	public class XMLConfigFactory
	{
		
		public function getLoggerScopes(xml:XML):Array
		{
			var loggerInfos:Array = new Array()
				
			try{	
				var appenders:Dictionary = parseAppenders( xml )
				var loggerNodes:XMLList = xml.logger
				var root:XML = xml.root[0]
					
				var loggerScope:LoggerScope
				for each(var loggerNode:XML in loggerNodes){
					loggerScope = parseLoggerScopeNode(loggerNode, appenders)
					if(loggerScope){
						loggerInfos.push( loggerScope )
					}
				}
				
				if(root){
					loggerScope = parseLoggerScopeNode(root, appenders)
					if(loggerScope){
						loggerInfos.push( loggerScope )
					}
				}
			}catch(error:Error){
				LogUtils.writeError("Unknown error "+error.message)
			}
			
			return loggerInfos
		}
		
		private function parseAppenders(xml:XML):Dictionary
		{
			var appenderNodes:XMLList= xml.appender
			var appanders:Dictionary = new Dictionary()
			
			for each(var appenderNode:XML in appenderNodes){
				
				var className:String = appenderNode.attribute("class")
				var name:String = String(appenderNode.@name).toUpperCase()
				
				if(!name){
					LogUtils.writeError( "Appender must have a valid name \n"+ appenderNode.toXMLString())
					continue
				}	
				
				if(!className){
					LogUtils.writeError( "Appender "+name+" must have a valid class name" )
					continue
				}
					
				var AppenderClass:Class = LogUtils.getDef( className )
				if(AppenderClass){
					try{
						
						var appender:Appender = new AppenderClass(name) as Appender;
						if(appender){
							appender.layout = parseLayout(name, appenderNode)
							appanders[ name ] = appender
						}else{
							LogUtils.writeError(
								"Error when creating Appender: "+className+" must extend "+
								getQualifiedClassName( Appender )
							)
						}
							
					}catch(error:Error){
						LogUtils.writeError("Error when creating Appender "+name+" message:"+error.message)
					}
				}else{
					LogUtils.writeError("Appender class: "+className+" not found, make sure that the compiler added it")
				}
			}
			
			return appanders
		}
		
		private function parseLayout(parentName:String, node:XML):Layout
		{
			var layoutNodes:XMLList = node.layout
				
			if(layoutNodes.length()){
				var layoutNode:XML = layoutNodes[0]
				var className:String = layoutNode.attribute("class")
					
				if(!className){
					LogUtils.writeError( "Layout in "+parentName+" must have a valid class name" )
					return null
				}
				
				var LayoutClass:Class = LogUtils.getDef( className )
				if(LayoutClass){
					try{
						
						var layout:Layout = new LayoutClass() as Layout;
						if(layout){
							
							var properties:Object = parseCustomProperties(layoutNode.children())
							for(var key:String in properties){
								try{
									layout[ key ] = properties[ key ]
								}catch(error:Error){
									LogUtils.writeError( "Layout "+className+" has no property "+key)
								}
							}
							
							return layout
						}else{
							LogUtils.writeError(
								"Error when creating Layout: "+className+" must extend "+
								getQualifiedClassName( Layout )
							)
						}
						
					}catch(error:Error){
						LogUtils.writeError("Error when creating Layout class "+className+" message:"+error.message)
					}
				}else{
					LogUtils.writeError("Layout class: "+className+" not found, make sure that the compiler added it")
				}	
			}
			
			return null
		}
		
		private function parseCustomProperties(propNodes:XMLList):Object
		{
			var properties:Object = new Object()
			
			for each(var propNode:XML in propNodes){
				var name:String = propNode.name()
				var value:String = propNode.text()
				properties[ name ] = value
			}
			
			return properties
		}
		
		private function parseLoggerScopeNode(node:XML, appenders:Dictionary):LoggerScope
		{
			if(!node.hasComplexContent()) return null
			
			var nodeName:String = String(node.name()).toUpperCase();
			var name:String = String(node.@name).toUpperCase()
			var levelNodes:XMLList = node.level
			var appenderRefNodes:XMLList = node.child("appender-ref")
			
			var isRoot:Boolean
			if(nodeName == "ROOT"){
				isRoot = true
			}
			
			if(!isRoot && !name){
				LogUtils.writeError("Logger must have a valid name in:\n"+node.toXMLString())
				return null
			}
			
			if(levelNodes.length() != 1){
					if(isRoot){
						LogUtils.writeError("Root Logger must contain one level")
					}else{
						LogUtils.writeError("Logger "+name+" must contain one level")
					}
					return null
			}
			
			var levelNode:XML = levelNodes[0]
			var levelValue:String = String(levelNode.@value).toUpperCase()
			var level:Level = LevelHolder.getLevel( levelValue )
			if(!level){
				LogUtils.writeError("Unregistered level: "+levelValue)
				return null
			}
				
			var refedAppenders:Array = new Array()	
			for each(var appenderRefNode:XML in appenderRefNodes){
				var ref:String = String(appenderRefNode.@ref).toUpperCase()
				if(ref){
					var appender:Appender = appenders[ ref ]
					if(appender){
						refedAppenders.push( appender )
					}else{
						LogUtils.writeError("Rererenced appender "+ref+" not found")
					}
					
				}else{
					LogUtils.writeError("Appender reference must be set in Logger "+name)
				}
			}
			
			var loggerScope:LoggerScope = new LoggerScope(isRoot, name, level, refedAppenders)
			
			return loggerScope;
		}
	}
}